/**
 * File: util.c
 *
 * Integrantes:
 * @author Jormar Arellano      (05-37840)  <jormar.arellano@gmail.com>
 * @author Sabrina Fernández    (05-38163)  <sabrinamfm@gmail.com>
 */

#include "util.h"
#include "graph.h"
#include "arraylist.h"
#include "LocalSearch.h"
#include "ILS.h"
#include "Grasp.h"
#include "Igral.h"
#include "Genetic.h"
#include "Scatter.h"

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <string.h>

void defaults() {
    srand(time(NULL));

    graphFile = NULL;
    _verbose = 0;
    _steps = 0;
    metaheuristic = local_search;
    initialSolution = iteratedGreedyAlgorithm;

    ls_defaults();
    ils_defaults();
	grasp_defaults();
    igral_defaults();
	genetic_defaults();
    scatter_defaults();
}

int validarArgs(int argc, char **argv) {
    long value;
    int i;

    for(i=1; i<argc; i++) {
        // Parametros generales
        if(i+1==argc && argv[i][0] != '-') {
            if(asprintf(&graphFile, "%s", argv[i]) < 0) {
                perror("Error al reservar memoria con nombre del archivo\n");
                exit(EXIT_FAILURE);
            }
            printf(" * Problema a resolver: %s\n", graphFile);

        } else if(0 == strcmp(argv[i], "-f")) {

            if(asprintf(&graphFile, "%s", argv[i+1]) < 0) {
                perror("Error al reservar memoria con nombre del archivo\n");
                exit(EXIT_FAILURE);
            }
            printf(" * Problema a resolver: %s\n", graphFile);
            i++;

        } else if(strcmp(argv[i], "-v") == 0) {
            if(!_verbose) printf(" * Activando modo verbose\n");
            _verbose++;
            ls_verbose++;
            ils_verbose++;
            scatter_verbose++;

        } else if(strcmp(argv[i], "-vv") == 0) {
            if(!_verbose) printf(" * Activando modo verbose\n");
            _verbose+=2;
            ls_verbose+=2;
            ils_verbose+=2;
            scatter_verbose+=2;

        } else if(strcmp(argv[i], "-vvv") == 0) {
            if(!_verbose) printf(" * Activando modo verbose\n");
            _verbose+=3;
            ls_verbose+=3;
            ils_verbose+=3;
            scatter_verbose+=3;

        } else if(strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--step") == 0) {
            _steps = 1;
            printf(" * Activando las PAUSAS en ejecucion\n");

        } else if( strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-?") == 0) {
            printHelp();
            exit(EXIT_SUCCESS);
        }

        // Soluciones Iniciales
        else if(strcmp(argv[i], "--initial-worst") == 0) {
            initialSolution = initialSolution_worst;
            printf(" * Solucion inicial Peor Caso\n");

        } else if(strcmp(argv[i], "--initial-greedy") == 0) {
            initialSolution = iteratedGreedyAlgorithm;
            printf(" * Solucion inicial Greedy\n");
        }

        // Seleccion de heuristica
        else if(strcmp(argv[i], "--ls") == 0) {
            metaheuristic = local_search;
            printf(" * Aplicar LOCAL SEARCH (LS)\n");

        } else if(strcmp(argv[i], "--ils") == 0) {
            metaheuristic = ils;
            printf(" * Aplicar ILS\n");

            // Adicional a esto, ILS establece otros defaults
            ls_iter = 1000;
            printf("      LS: %ld iteraciones para LS\n", ls_iter);

        } else if(strcmp(argv[i], "--grasp") == 0) {
            metaheuristic = grasp;
            printf(" * Aplicar Grasp\n");

        } else if(strcmp(argv[i], "--igral") == 0) {
            metaheuristic = igral;
            printf(" * Aplicar Iterative Greedy Algorithm (IGrAl)\n");

        } else if(strcmp(argv[i], "--scatter") == 0) {
            metaheuristic = scatter;
            printf(" * Aplicar Scatter\n");
        } else if(strcmp(argv[i], "--genetic") == 0) {
            metaheuristic = genetic;
            printf(" * Aplicar Genetic Algorithm (GA)\n");

        } else if(strcmp(argv[i], "--greedy") == 0) {
            metaheuristic = iteratedGreedyAlgorithm;
            printf(" * Aplicar UNICAMENTE algoritmo greedy\n");
        }

        // Parametros para LOCAL SEARCH
        else if(strcmp(argv[i], "--ls-iter") == 0) {
            value = atoi(argv[i+1]);
            if(value != 0) ls_iter = value;
            printf(" * Esperar %ld iteraciones en LOCAL SEARCH.\n", ls_iter);
            i++;

        } else if(strcmp(argv[i], "--ls-accept-penalty") == 0) {
            ls_acceptance = ls_acceptance_penaltyFunction;
            printf(" * LS: Criterio de aceptacion con funcion de penalidad\n");

        } else if(strcmp(argv[i], "--ls-accept-bestfeasible") == 0) {
            ls_acceptance = ls_acceptance1;
            printf(" * LS: Criterio de aceptacion mejor solucion factible\n");

        } else if(strcmp(argv[i], "--ls-accept-minconflict") == 0) {
            ls_acceptance = ls_acceptance_minConflicts;
            printf(" * LS: Criterio de aceptacion min comflicts\n");

        } else if(strcmp(argv[i], "--ls-neighbor-oneexchange") == 0) {
            ls_getNeighbor = getNeighbor_oneExchange;
            printf(" * LS: Vecino calculado por One Exchange\n");

        } else if(strcmp(argv[i], "--ls-neighbor-minconflict") == 0) {
            ls_getNeighbor = getNeighbor_minConflicts;
            printf(" * LS: Vecino calculado por Minimizacion de conflictos\n");

        }

        // Parametros para ILS
        else if(strcmp(argv[i], "--ils-iter") == 0) {
            value = atoi(argv[i+1]);
            if(value != 0) ils_iter = value;
            printf(" * Esperar %ld iteraciones en ILS\n", ils_iter);
            i++;

        } else if(strcmp(argv[i], "--ils-disturb-random") == 0) {
            value = atoi(argv[i+1]);
            if(value != 0) ils_recoloring_k = (int)value;

            printf(" * ILS: Perturbacion con %d random recoloring\n",
                    ils_recoloring_k);
            i++;

        } else if(strcmp(argv[i], "--ils-accept-best") == 0) {
            ils_acceptance = acceptance_thebest;
        }

		//Parametros para Grasp
		else if(strcmp(argv[i], "--grasp-giter") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) grasp_Giter = (int)value;
			printf(" * Grasp contendra %d iteraciones\n", grasp_Giter);
			i++;
		}
		else if(strcmp(argv[i], "--grasp-citer") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) grasp_Citer = (int)value;
			printf(" * Grasp contendra %d iteraciones por color\n", grasp_Citer);
			i++;
		}
		else if(strcmp(argv[i], "--grasp-csize") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) grasp_Csize = (int)value;
			printf(" * Tamaño de la lista de candidatos: %d\n", grasp_Csize);
			i++;
		}

		//Parametros para IGrAl
		else if(strcmp(argv[i], "--igral-iter") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) igral_iter = (int)value;
			printf(" * IGrAl esperara %d iteraciones sin mejora\n", igral_iter);
			i++;
		}
		else if(strcmp(argv[i], "--igral-k1") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) igral_iter = (int)value;
			printf(" * IGrAl: %d%% de usar alg. greedy para eliminar infactibilidad\n",
                    igral_k1);
			i++;
		}
		else if(strcmp(argv[i], "--igral-k2") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) igral_iter = (int)value;
			printf(" * IGrAl: %d%% de detener generacion de sol infactibles\n",
                    igral_k2);
			i++;
		}

		// Parametros para el algoritmo Genetico
		else if(strcmp(argv[i], "--genetic-Psize") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) genetic_Psize = (int)value;
			printf(" * GA tendra %d cromosomas\n", genetic_Psize);
			i++;
		}
		else if(strcmp(argv[i], "--genetic-Giter") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) genetic_Giter = (int)value;
			printf(" * GA hara %d iteraciones\n", genetic_Giter);
			i++;
		}
		else if(strcmp(argv[i], "--genetic-MOperc") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) genetic_MOperc = (double)value;
			printf(" * Continuaran %f cromosomas en la siguiente poblacion\n", genetic_MOperc);
			i++;
		}
		else if(strcmp(argv[i], "--genetic-COperc") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) genetic_COperc = (double)value;
			printf(" * Se cruzaran %f cromosomas\n", genetic_COperc);
			i++;
		}
		else if(strcmp(argv[i], "--genetic-firstPoint") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) firstPointCut = (int)value;
			printf(" * El primer punto de cruce es %d\n", firstPointCut);
			i++;
		}
		else if(strcmp(argv[i], "--genetic-secondPoint") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) secondPointCut = (int)value;
			printf(" * El segundo punto de cruce es %d\n", secondPointCut);
			i++;
		}
		else if(strcmp(argv[i], "--genetic-mutationPoint") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) mutationPoint = (int)value;
			printf(" * Se haran %d perturbaciones sobre los cromosomas que mutaran\n", mutationPoint);
			i++;
		}
        // Parametros para Scatter
		else if(strcmp(argv[i], "--scatter-population") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) scatter_population_size = (int)value;
			printf(" * Scatter: Poblacion Inicial %d\n",
                    scatter_population_size);
			i++;
		}
		else if(strcmp(argv[i], "--scatter-k") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) scatter_initial_k = (int)value;
			printf(" * Scatter: MAX %d de colores generados por diversificacion\n",
                    scatter_initial_k);
			i++;
		}
		else if(strcmp(argv[i], "--scatter-b") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) scatter_b = (int)value;
			printf(" * Scatter: Escoger %d mejores individuos\n",
                    scatter_b);
			i++;
		}
		else if(strcmp(argv[i], "--scatter-d") == 0) {
			value = atoi(argv[i+1]);
			if(value !=0 ) scatter_d = (int)value;
			printf(" * Scatter: Escoger %d individuos mas diversos\n",
                    scatter_d);
			i++;
		}

        // La opcion es desconocida
        else {
            printf(" >> Opcion %s no valida\n", argv[i]);
        }
    }

    // Verificaciones finales
    if(!graphFile){
        printf("Uso: coloracion [OPTIONS] FILE\n");
        printf("Pruebe `./coloracion --help` para mas informacion\n");
        exit(EXIT_SUCCESS);
    }

    return 1;
}

void printHelp() {
    FILE *helpFile;
    char *line = NULL;
    size_t nline = 0;

    if( !(helpFile = fopen("help.txt", "r")) ) {
        perror("No se puede leer el archivo de ayuda\n");
        exit(EXIT_FAILURE);
    }
    while( getline(&line, &nline, helpFile) != -1 ) printf("%s", line);
    if(line) free(line);
}

void waitStep() {
    printf("\nSTEP: presione ENTER para continuar\n");
    getchar();
}

ArrayList *newNumerationArray(int n) {
    int i, *value;
    ArrayList *list = arraylist_new(n);

    for(i=0; i<n; i++){
        value = malloc(sizeof(int));
        *value = i+1;
        arraylist_append(list, value);
    }

    return list;
}

void freeNumerationArray(ArrayList *l) {
    int i, n;

    for(i=0, n=l->length; i<n; i++) free(l->data[i]);
    arraylist_free(l);
}

void permuteArray(ArrayList *v) {
    int i, n;
    int r1, r2;
    ArrayListValue *temp = NULL;

    for(i=0, n=v->length*4; i<n; i++) {
        r1 = rand() % v->length;
        r2 = rand() % v->length;

        temp = v->data[r1];
        v->data[r1] = v->data[r2];
        v->data[r2] = temp;
    }
}

Graph *iteratedGreedyAlgorithm(Graph *graph){
    int i, j, color;
    int k;
    int conflicto = 0;
    Vertex *v, *neighbor;
    ArrayList *pi = newNumerationArray(graph->vertex_number-1);

    permuteArray(pi);
    k=1;
    setColor(graph, graph->vertex->data[0], 1);

    // Asignamos color a cada vertice
    for(i=0; i < pi->length; i++){
        v = graph->vertex->data[ (*(int*)pi->data[i]) ];
        // Elegimos el color mas pequeno sin conflicto
        for(color=1; color<=k+1; color++){
            conflicto = 0;  // false
            for(j=0; j<v->neighbors->length; j++){
                neighbor = v->neighbors->data[j];
                if( neighbor->color == color){
                    conflicto = 1;
                    break;
                }
            }
            if(!conflicto){
                setColor(graph, v, color);
                if (k<color) k=color;
                break;
            }
        }
    }
    arraylist_free(pi);

    return graph;
}

Graph *initialSolution_worst(Graph *graph) {
    int i;
    Vertex *v;
    ArrayList *pi = newNumerationArray(graph->vertex_number);

    for(i=0; i<pi->length; i++) {
        v = graph->vertex->data[(*(int *)pi->data[i])-1];
        setColor(graph, v, i+1);
    }
    arraylist_free(pi);

    return graph;
}
